import asyncio
import logging

from aiogram import Bot, Dispatcher
from aiogram.fsm.storage.memory import MemoryStorage, SimpleEventIsolation
from aiogram.fsm.storage.redis import DefaultKeyBuilder, RedisStorage
from sqlalchemy.ext.asyncio import async_sessionmaker

from tinkoff_bot.config import load_config
from tinkoff_bot.infrastructure.database.main import build_sa_engine, build_sa_session_factory
from tinkoff_bot.infrastructure.logger import configure_logging
from tinkoff_bot.infrastructure.tinkoff_kassa.api import TinkoffKassa
from tinkoff_bot.tg_bot.handlers import register_handlers
from tinkoff_bot.tg_bot.middlewares.db import DBSessionMiddleware

logger = logging.getLogger(__name__)


def setup_middlewares(
    dp: Dispatcher,
    session_factory: async_sessionmaker,
):
    dp.callback_query.outer_middleware(DBSessionMiddleware(session_factory))
    dp.update.outer_middleware(DBSessionMiddleware(session_factory))


async def main():
    config = load_config()
    configure_logging()

    if config.tg_bot.use_redis:
        storage = RedisStorage.from_url(
            url=config.redis.url,
            key_builder=DefaultKeyBuilder(with_destiny=True),
        )
    else:
        storage = MemoryStorage()

    bot = Bot(token=config.tg_bot.token, parse_mode="HTML")
    bot_username = await bot.get_me()
    logger.error(f"Starting bot {bot_username}")

    dp = Dispatcher(storage=storage, events_isolation=SimpleEventIsolation())
    payment_client = TinkoffKassa(config.payment.terminal_key, config.payment.password)
    engine = build_sa_engine(config.db)
    session_factory = build_sa_session_factory(engine)

    setup_middlewares(dp, session_factory)
    register_handlers(dp)

    try:
        await dp.start_polling(bot, config=config, payment_client=payment_client)
    finally:
        await engine.dispose()


def cli():
    try:
        asyncio.run(main())
    except (KeyboardInterrupt, SystemExit):
        logger.error("Bot stopped!")


if __name__ == "__main__":
    cli()
